package com.example.extenddemo.handle;

import cn.hutool.core.map.MapUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.example.extenddemo.config.SpEL;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.expression.BeanResolver;
import org.springframework.expression.Expression;
import org.springframework.expression.ParserContext;
import org.springframework.expression.common.TemplateParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

@Aspect
@Slf4j
@Component
public class SpELHandler {
    ThreadLocal<Map<String,Object>> data = new ThreadLocal<>();
    // Spring单例Bean的变量 在操作这样的对象时要考虑多线程并发的情况
//
//    /**
//     * * 定义spel表达式解析器
//     */
//    private final ExpressionParser parser = new SpelExpressionParser();
//    /**
//     * * 定义spel解析模版
//     */
//    private final ParserContext parserContext = new TemplateParserContext();
//    /**
//     *  定义spel上下文对象进行解析 可以注册方法
//     */
//    private final ThreadLocal<StandardEvaluationContext> context = new ThreadLocal<>();
//    /**
//     * * 方法参数解析器 主要点
//     */
//    private final ParameterNameDiscoverer pnd = new DefaultParameterNameDiscoverer();
//
    //解决多线程并发问题的版本
    private void setValue(){
        Map<String,Object> map = new HashMap<>();
        map.put("parse",new SpelExpressionParser());
        map.put("context",new StandardEvaluationContext());
        map.put("parserContext",new TemplateParserContext());
        map. put("pnd",new DefaultParameterNameDiscoverer());
        data.set(map);
    }

    @Before(value = "@annotation(spEL)")
    public void before(JoinPoint joinPoint, SpEL spEL) {
        setValue();
        SpelExpressionParser parser = MapUtil.get(data.get(), "parse", SpelExpressionParser.class);
        StandardEvaluationContext context = MapUtil.get(data.get(), "context", StandardEvaluationContext.class);
        ParserContext parserContext = MapUtil.get(data.get(), "parserContext", ParserContext.class);
        DefaultParameterNameDiscoverer pnd = MapUtil.get(data.get(), "pnd", DefaultParameterNameDiscoverer.class);
        BeanResolver resolver = new BeanFactoryResolver(SpringUtil.getBeanFactory());
        // 赋予标准上下文解析器解析Bean对象的能力
        context.setBeanResolver(resolver);
        log.info("SpELHandler before key：{}",spEL.value());
        String value = spEL.value();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        Class<?> target = method.getDeclaringClass();
        Object[] args = joinPoint.getArgs();
        String[] argNames = pnd.getParameterNames(method);
        for (int i = 0; i < args.length; i++){
            assert argNames != null;
            log.info("agrName: {},agr: {}", argNames[i], args[i]);
            context.setVariable(argNames[i], args[i]);
        }
        String key = null;
        Expression expression = null;
        // * 如果是#{} 表示时计算表达式 也可以是其他方法的调用 getExpressionPrefix()-> #{   getExpressionSuffix()-> }
        if(value.startsWith(parserContext.getExpressionPrefix()) && value.endsWith(parserContext.getExpressionSuffix())){
            expression = parser.parseExpression(value, parserContext);
        }else{
            // * # 开头的是直接解析属性
            expression = parser.parseExpression(value);
        }
        key = expression.getValue(context, String.class);
        log.info("解析后的key为：{}",key);
    }

    @After(value = "@annotation(spEL)")
    public void after(SpEL spEL){
        data.remove();
    }

    /**
     * @Describe: <p style="color:blue;">抛出异常后的处理</p>
     * @author 郭敖
     * @date 23:12 2024/4/28
     * @param joinPoint
     * @param spEL
     */
    @AfterThrowing("@annotation(spEL)")
    public void afterThrowing(JoinPoint joinPoint, SpEL spEL){
        data.remove();
    }
}
